package com.diy.sigmund.diycommon.spring.interceptor;

import com.diy.sigmund.diycommon.entity.constant.LogConstant;
import com.diy.sigmund.diycommon.spring.anno.ApiIdempotent;
import com.diy.sigmund.diycommon.middleware.cache.redis.RedisService;
import com.diy.sigmund.diycommon.spring.service.ApiIdempotentService;
import java.lang.reflect.Method;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * 幂等性接口拦截器
 *
 * @author ylm-sigmund
 * @since 2021/7/29 22:13
 */
public class ApiIdempotentInterceptor implements HandlerInterceptor {

    private static final Logger log = LoggerFactory.getLogger(ApiIdempotentInterceptor.class);

    @Autowired
    private RedisService redisService;
    @Autowired
    private ApiIdempotentService apiIdempotentService;

    /**
     * preHandle是在请求执行前执行的
     *
     * @param request  request
     * @param response response
     * @param handler  handler
     * @return 返回true, postHandler和afterCompletion方法才能执行, 否则false为拒绝执行，起到拦截器控制作用
     * @throws Exception Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        log.info(LogConstant.SPRING_LOG, "幂等性接口拦截器处理 开始");
        /**
         * handler保存了本次请求的controller也就是接口方法的一些信息，如类，方法，参数等
         * 如果是一次静态资源的请求则该handler不应该是HandlerMethod的实现类
         * 判断是否是一个正常的接口，如果是则进行鉴权操作，不是则直接放行
         */
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        if (!method.isAnnotationPresent(ApiIdempotent.class)) {
            return true;
        }
        // 获取方法上的注解
        ApiIdempotent apiIdempotent = method.getAnnotation(ApiIdempotent.class);
        // 如果标记了注解，则判断权限
        if (Objects.isNull(apiIdempotent)) {
            return true;
        }
        try {
            // 幂等性校验, 校验通过则放行, 校验失败则抛出异常, 并通过统一异常处理返回友好提示
            return apiIdempotentService.checkToken(request);
        } catch (Exception ex) {
            throw ex;
        }
    }

    /**
     * postHandler是在请求结束之后,视图渲染之前执行的,但只有preHandle方法返回true的时候才会执行
     *
     * @param request      request
     * @param response     response
     * @param handler      handler
     * @param modelAndView modelAndView
     * @throws Exception Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {

    }

    /**
     * afterCompletion是视图渲染完成之后才执行,同样需要preHandle返回true，
     *
     * @param request  request
     * @param response response
     * @param handler  handler
     * @param ex       Exception
     * @throws Exception Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
            Object handler, Exception ex)
            throws Exception {
        log.info(LogConstant.SPRING_LOG, "幂等性接口拦截器处理 结束");
    }

}
